/**
 * \file tuya_ble_app_uart_common_handler.c
 *
 * \brief
 */
/*
 *  Copyright (C) 2014-2019, Tuya Inc., All Rights Reserved
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may
 *  not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  This file is part of tuya ble sdk
 */

#include "tuya_ble_stdlib.h"
#include "tuya_ble_type.h"
#include "tuya_ble_heap.h"
#include "tuya_ble_mem.h"
#include "tuya_ble_api.h"
#include "tuya_ble_port.h"
#include "tuya_ble_main.h"
#include "tuya_ble_internal_config.h"
#include "tuya_ble_data_handler.h"
#include "tuya_ble_mutli_tsf_protocol.h"
#include "tuya_ble_utils.h"
#include "tuya_ble_secure.h"
#include "tuya_ble_main.h"
#include "tuya_ble_storage.h"
#include "tuya_ble_app_uart_common_handler.h"
#include "tuya_ble_log.h"
#include "ty_ble.h"
#include "board.h"
#include "peripheral.h"


#define UART_SUCCESS                0
#define UART_FAIL                   1

#define MCU_OTA_DATA_LENGTH_MAX     200

#define TUYA_BLE_OTA_MCU_TYPE       1

#define PIN_LOWPOWER_CTRL           4
#define PIN_LOWPOWER_CTRL_MASK      (BITMASK(PIN_LOWPOWER_CTRL))


static tuya_ble_timer_t heartbeat_timer;
static uint32_t dp_sn = 0;
static uint8_t mcu_info_get_flag;
static uint8_t lowpower_flag;


static void tuya_ble_common_uart_protocol_send(uint8_t cmd, uint8_t *pdata, uint8_t len)
{
    static uint8_t alloc_buf[256];

    alloc_buf[0] = 0x55;
    alloc_buf[1] = 0xaa;
    alloc_buf[2] = 0x00;
    alloc_buf[3] = cmd;
    alloc_buf[4] = 0;
    alloc_buf[5] = len;

    memcpy(alloc_buf+6, pdata, len);
    alloc_buf[len+6] = tuya_ble_check_sum(alloc_buf, len+6);

    tuya_ble_common_uart_send_data(alloc_buf, len+7);
}


static void tuya_ble_uart_common_resp_to_mcu(uint8_t cmd_type, uint8_t state)
{
    // 0 - success; other - faile
    uint8_t temp = state;
    tuya_ble_common_uart_protocol_send(cmd_type, &temp, 1);
}


static void tuya_ble_heartbeat_timer_callback(tuya_ble_timer_t timer)
{
    tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_HEARTBEAT, NULL, 0); // send heartbeat message
}


static void tuya_ble_heartbeat_timer_create(void)
{
    if(tuya_ble_timer_create(&heartbeat_timer, 3000,
       TUYA_BLE_TIMER_REPEATED, tuya_ble_heartbeat_timer_callback) != TUYA_BLE_SUCCESS)
    {
        TUYA_BLE_LOG_ERROR("heartbeat_timer creat failed");
    }
}


static void tuya_ble_heartbeat_timer_start(void)
{
    if(tuya_ble_timer_start(heartbeat_timer) != TUYA_BLE_SUCCESS)
    {
        TUYA_BLE_LOG_ERROR("heartbeat_timer start failed");
    }
}


static void tuya_ble_heartbeat_timer_restart(uint32_t ms)
{
    if(tuya_ble_timer_restart(heartbeat_timer,ms) != TUYA_BLE_SUCCESS)
    {
        TUYA_BLE_LOG_ERROR("heartbeat_timer restart failed");
    }
}


static void tuya_ble_heartbeat_timer_stop(void)
{
    if(tuya_ble_timer_stop(heartbeat_timer) != TUYA_BLE_SUCCESS)
    {
        TUYA_BLE_LOG_ERROR("heartbeat_timer stop failed");
    }
}


static void tuya_ble_heartbeat_timer_delete(void)
{
    if(tuya_ble_timer_delete(heartbeat_timer) != TUYA_BLE_SUCCESS)
    {
        TUYA_BLE_LOG_ERROR("heartbeat_timer delete failed");
    }
}


static void gpio_int_handler(uint32_t pin_mask)
{
    if (lowpower_flag == 0)
    {
        TY_PRINTF("enter low power mode");
        lowpower_flag = 1;

        tuya_ble_heartbeat_timer_stop();

        ty_ble_adv_param_t param = {
            .adv_interval_min = 1000,
            .adv_interval_max = 1000,
            .adv_type = 0x03,
        };
        ty_ble_set_adv_param(&param);
        ty_ble_start_adv();
    }
}


static void wakeup_handler(uint32_t pin_mask)
{
    if (lowpower_flag == 1)
    {
        TY_PRINTF("enter normal power mode");
        lowpower_flag = 0;

        if (mcu_info_get_flag == 0)
        {
            tuya_ble_heartbeat_timer_restart(3000);
        }
        else
        {
            tuya_ble_heartbeat_timer_restart(10000);
//            tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_HEARTBEAT, NULL, 0); // send heartbeat message
        }

        ty_ble_adv_param_t param = {
            .adv_interval_min = 100,
            .adv_interval_max = 100,
            .adv_type = 0x03,
        };
        ty_ble_set_adv_param(&param);
        ty_ble_start_adv();
    }
}


void tuya_Enter_or_Exit_Sleep(uint8_t en)
{
   if(en)
   {
        tuya_ble_heartbeat_timer_stop();//gpio_int_handler(0);
        
   }
   else
   {
        tuya_ble_heartbeat_timer_restart(10000);//wakeup_handler(0);
   }
  
}


static void tuya_ble_uart_common_mcu_ota_data_from_uart_handler(uint8_t cmd, uint8_t * data_buffer,uint16_t data_len)
{
    static uint8_t ble_data_buffer[30];
    static uint8_t ble_data_len = 0;
    uint16_t ble_cmd = 0;
    tuya_ble_connect_status_t currnet_connect_status;

    memset(ble_data_buffer, 0, sizeof(ble_data_buffer));
    ble_data_len = 0;

    switch(cmd)
    {
        case UART_COMMON_CMD_OTA_REQUEST:
            ble_data_buffer[0] = data_buffer[0];
            ble_data_buffer[1] = 3;
            ble_data_buffer[2] = TUYA_BLE_OTA_MCU_TYPE;
            ble_data_buffer[3] = 0;
            memcpy(&ble_data_buffer[4], data_buffer+1, 5);
            ble_data_len = 9;
            ble_cmd = FRM_OTA_START_RESP;
            break;

        case UART_COMMON_CMD_OTA_FILE_INFO:
            ble_data_buffer[0] = TUYA_BLE_OTA_MCU_TYPE;
            memcpy(&ble_data_buffer[1], data_buffer, 25);
            ble_data_len = 26;
            ble_cmd = FRM_OTA_FILE_INFOR_RESP;
            break;

        case UART_COMMON_CMD_OTA_FILE_OFFSET:
            ble_data_buffer[0] = TUYA_BLE_OTA_MCU_TYPE;
            memcpy(&ble_data_buffer[1], data_buffer, 4);
            ble_data_len = 5;
            ble_cmd = FRM_OTA_FILE_OFFSET_RESP;
            break;

        case UART_COMMON_CMD_OTA_DATA:
            ble_data_buffer[0] = TUYA_BLE_OTA_MCU_TYPE;
            ble_data_buffer[1] = data_buffer[0];
            ble_data_len = 2;
            ble_cmd = FRM_OTA_DATA_RESP;
            break;

        case UART_COMMON_CMD_OTA_END:
            ble_data_buffer[0] = TUYA_BLE_OTA_MCU_TYPE;
            ble_data_buffer[1] = data_buffer[0];
            ble_data_len = 2;
            ble_cmd = FRM_OTA_END_RESP;
            break;

        default:
            break;
    };

    currnet_connect_status = tuya_ble_connect_status_get();

    if(currnet_connect_status != BONDING_CONN)
    {
        TUYA_BLE_LOG_ERROR("tuya_ble_uart_common_mcu_ota_process FAILED.");
        return;
    }

    if(ble_data_len > 0)
    {
        tuya_ble_commData_send(ble_cmd, 0, ble_data_buffer, ble_data_len, ENCRYPTION_MODE_SESSION_KEY);
    }
}


void tuya_ble_uart_common_mcu_ota_data_from_ble_handler(uint16_t cmd, uint8_t * recv_data,uint32_t recv_len)
{
    static uint8_t uart_data_temp[42];
    uint8_t *uart_data_buffer = NULL;
    uint8_t uart_data_len = 0;

    if(cmd == FRM_OTA_DATA_REQ)
    {
        uart_data_buffer = (uint8_t*)tuya_ble_malloc(recv_len + 7);
        if(uart_data_buffer==NULL)
        {
            TUYA_BLE_LOG_ERROR("uart_data_buffer malloc failed.\n");
            return;
        }
    }
    else
    {
        uart_data_buffer = uart_data_temp;
    }

    uart_data_buffer[0] = 0x55;
    uart_data_buffer[1] = 0xAA;
    uart_data_buffer[2] = 0x00;
    switch (cmd)
    {
        case FRM_OTA_START_REQ:
            uart_data_buffer[3] = UART_COMMON_CMD_OTA_REQUEST;
            uart_data_buffer[4] = 0;
            uart_data_buffer[5] = 2;
            uart_data_buffer[6] = MCU_OTA_DATA_LENGTH_MAX>>8;
            uart_data_buffer[7] = (uint8_t)MCU_OTA_DATA_LENGTH_MAX;
            uart_data_len = 8;
            break;

        case FRM_OTA_FILE_INFOR_REQ:
            uart_data_buffer[3] = UART_COMMON_CMD_OTA_FILE_INFO;
            uart_data_buffer[4] = 0;
            uart_data_buffer[5] = 35;
            memcpy(uart_data_buffer+6, recv_data, 8);
            uart_data_buffer[14] = recv_data[9];
            uart_data_buffer[15] = recv_data[10];
            uart_data_buffer[16] = recv_data[11];
            memcpy(&uart_data_buffer[17], recv_data+12, 24);
            uart_data_len = 41;
            break;

        case FRM_OTA_FILE_OFFSET_REQ:
            uart_data_buffer[3] = UART_COMMON_CMD_OTA_FILE_OFFSET;
            uart_data_buffer[4] = 0;
            uart_data_buffer[5] = 4;
            memcpy(uart_data_buffer+6, recv_data, 4);
            uart_data_len = 10;
            break;

        case FRM_OTA_DATA_REQ:
            uart_data_buffer[3] = UART_COMMON_CMD_OTA_DATA;
            uart_data_buffer[4] = 0;
            uart_data_buffer[5] = recv_len;
            memcpy(uart_data_buffer+6, recv_data, recv_len);
            uart_data_len = 6+recv_len;
            break;

        case FRM_OTA_END_REQ:
            uart_data_buffer[3] = UART_COMMON_CMD_OTA_END;
            uart_data_buffer[4] = 0;
            uart_data_buffer[5] = 0;
            uart_data_len = 6;
            break;

        default:
            break;
    }

    uart_data_buffer[uart_data_len] = tuya_ble_check_sum(uart_data_buffer, uart_data_len);

    tuya_ble_common_uart_send_data(uart_data_buffer, uart_data_len+1);

    TUYA_BLE_LOG_HEXDUMP_DEBUG("mcu ota uart send data : ", uart_data_buffer, uart_data_len+1);

    if(cmd == FRM_OTA_DATA_REQ)
    {
        tuya_ble_free(uart_data_buffer);
    }

}


void tuya_ble_uart_common_send_current_ble_state(void)
{
    uint8_t ble_work_state = 0;
    tuya_ble_connect_status_t ble_state;

    ble_state = tuya_ble_connect_status_get();

    if((ble_state==UNBONDING_UNCONN)||(ble_state==UNBONDING_UNCONN)||(ble_state==UNBONDING_UNCONN))
    {
        ble_work_state = 1;
    }
    else if((ble_state==BONDING_UNCONN)||(ble_state==BONDING_UNAUTH_CONN))
    {
        ble_work_state = 2;
    }
    else if(ble_state==BONDING_CONN)
    {
        ble_work_state = 3;
    }
    else
    {
        ble_work_state = 0;
    }

    tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_SEND_MODULE_WORK_STATE,(uint8_t *)&ble_work_state,1);
}


void tuya_ble_uart_common_process(uint8_t * p_in_data, uint16_t in_len)
{
    uint8_t cmd = p_in_data[3];
    uint16_t data_len = (p_in_data[4]<<8) + p_in_data[5];
    uint8_t *data_buffer = p_in_data+6;
    // TUYA_APP_LOG_HEXDUMP_INFO("UART RECV", p_in_data, in_len);

    switch(cmd)
    {
        case UART_COMMON_CMD_HEARTBEAT:
        {
            if (p_in_data[6] == 0)
            {
                TY_PRINTF("MCU has reboot");
                if (mcu_info_get_flag == 0)
                {
                    mcu_info_get_flag = 1;
                    if (lowpower_flag == 0) // normal power mode
                    {
                        tuya_ble_heartbeat_timer_restart(10000);
                    }
                    else // low power mode
                    {
                        tuya_ble_heartbeat_timer_stop();
                    }
                    tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_GET_MCU_INFO, NULL, 0);
                }
                else
                {
                    tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_QUERY_STATUS, NULL, 0);
                }
            }
        }   break;

        case UART_COMMON_CMD_GET_MCU_INFO:
        {
            uint8_t pid[9] = {0};
            memcpy(pid, p_in_data+6, 8);
            TY_PRINTF("PID: %s", pid);
            tuya_ble_device_update_product_id(TUYA_BLE_PRODUCT_ID_TYPE_PID, 8, pid);
            tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_GET_MODULE_WORK_MODE, NULL, 0);
        }   break;

        case UART_COMMON_CMD_GET_MODULE_WORK_MODE:
        {
            // 0 - unbonded; 1 - bonded but not connected; 2 - bonded and connected
            uint8_t work_state = 0;
            tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_SEND_MODULE_WORK_STATE, &work_state, 1);
        }   break;

        case UART_COMMON_CMD_SEND_MODULE_WORK_STATE:
        {
            if (data_buffer[0] == 0)
            {
                TY_PRINTF("Unbonded");
            }
            else if (data_buffer[0] == 1)
            {
                TY_PRINTF("Bonded, but not connected");
            }
            else if (data_buffer[0] == 2)
            {
                TY_PRINTF("Bonded, and connected");
            }
        }   break;

        case UART_COMMON_CMD_RESET_MODULE:
        case UART_COMMON_CMD_RESET_MODULE_NEW:
        {
            ty_system_reset();
        }   break;

        case UART_COMMON_CMD_SEND_CMD:
        {
            // should not reach here
        }   break;

        case UART_COMMON_CMD_REPORT_STATUS:
        {
            for (int i = 0; i < data_len;)
            {
                uint8_t dp_id = data_buffer[i+0];
                uint8_t dp_type = data_buffer[i+1];
                uint16_t dp_len = data_buffer[i+2]<<8 | data_buffer[i+3];
                // TUYA_APP_LOG_HEXDUMP_DEBUG("dp report", data_buffer+i, dp_len+4);
                tuya_ble_dp_data_send(dp_sn++, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL,
                                      DP_SEND_WITHOUT_RESPONSE, data_buffer+i, dp_len+4);
                i += dp_len + 4;
            }

            tuya_ble_uart_common_resp_to_mcu(UART_COMMON_CMD_REPORT_STATUS, UART_SUCCESS);
        }   break;

        case UART_COMMON_CMD_QUERY_STATUS:
        {
            // should not reach here
        }   break;

        case UART_COMMON_CMD_UNBIND_MODULE:
        {
            tuya_ble_device_unbind();
            ty_ble_disconnect();

            tuya_ble_uart_common_resp_to_mcu(UART_COMMON_CMD_UNBIND_MODULE, UART_SUCCESS);
        }   break;

        case UART_COMMON_CMD_GET_MODULE_CONN_STATE:
        {
            // should not reach here
        }   break;

        case UART_COMMON_CMD_GET_MODULE_VERSION:
        {
            uint8_t ver[6] = {0x01, 0x00, 0x00, 0x01, 0x00, 0x00};
            tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_GET_MODULE_VERSION, ver, 6);
        }   break;

        case UART_COMMON_CMD_RESTORE_FACTORY:
        {
            // should not reach here
        }   break;

        case UART_COMMON_CMD_REPORT_RECORD_DATA:
        {

            tuya_ble_uart_common_resp_to_mcu(UART_COMMON_CMD_REPORT_RECORD_DATA, UART_SUCCESS);
        }   break;

        case UART_COMMON_CMD_GET_TIME:
        {

        }   break;

        case UART_COMMON_CMD_GET_MCU_VERSION:
        case UART_COMMON_CMD_SEND_MCU_VERSION:
        {
            uint32_t mcu_firmware_version = data_buffer[0]<<16 | data_buffer[1]<<8 | data_buffer[2];
            uint32_t mcu_hardware_version = data_buffer[3]<<16 | data_buffer[4]<<8 | data_buffer[5];
            tuya_ble_device_update_mcu_version(mcu_firmware_version, mcu_hardware_version);
            TY_PRINTF("FW ver: %06x, HW ver: %06x", mcu_firmware_version, mcu_hardware_version);

            if (cmd == UART_COMMON_CMD_SEND_MCU_VERSION)
            {
                tuya_ble_uart_common_resp_to_mcu(UART_COMMON_CMD_SEND_MCU_VERSION, UART_SUCCESS);
            }
        }   break;

        case UART_COMMON_CMD_OTA_REQUEST:
        case UART_COMMON_CMD_OTA_FILE_INFO:
        case UART_COMMON_CMD_OTA_FILE_OFFSET:
        case UART_COMMON_CMD_OTA_DATA:
        case UART_COMMON_CMD_OTA_END:
        {
            tuya_ble_uart_common_mcu_ota_data_from_uart_handler(cmd,data_buffer,data_len);
        }   break;

        default:
            break;
    };

}


void tuya_ble_uart_common_init(void)
{
//    pinmux_config(PIN_LOWPOWER_CTRL, PINMUX_GPIO_MODE_CFG);
//    pmu_pin_mode_set(PIN_LOWPOWER_CTRL_MASK, PMU_PIN_MODE_PU);
//    gpio_set_direction(PIN_LOWPOWER_CTRL_MASK, GPIO_INPUT);

//    gpio_set_interrupt(PIN_LOWPOWER_CTRL_MASK, GPIO_RISING_EDGE);
//    gpio_set_interrupt_callback(gpio_int_handler);

//    pmu_wakeup_pin_set(PIN_LOWPOWER_CTRL_MASK, PMU_PIN_WAKEUP_LOW_LEVEL);
    pmu_wakeup_pin_register_callback(wakeup_handler, wakeup_handler);

    tuya_ble_heartbeat_timer_create();
//    if (gpio_read(PIN_LOWPOWER_CTRL_MASK) == 0)
    {
        TY_PRINTF("normal power mode");
        lowpower_flag = 0; // normal power mode
        tuya_ble_heartbeat_timer_start();
    }
//    else
//    {
//        TY_PRINTF("low power mode");
//        lowpower_flag = 1; // low power mode
//    }
    mcu_info_get_flag = 1;
}

void tuya_ble_common_uart_send_cmd(uint8_t *pdata, uint8_t len)
{
    tuya_ble_common_uart_protocol_send(UART_COMMON_CMD_SEND_CMD, pdata, len);
}
